

package net.seqalign;


import java.util.*;

import net.prot.*;
import net.matvec.*;


public class AlignmentSolution {

    protected List<StringBuilder> sequences;
    private boolean fitnessCalced = false;
    private double storedFitness;

    public AlignmentSolution(List<StringBuilder> seqs){
	sequences = seqs;
    }

    private AlignmentSolution(){
    }

    public AlignmentSolution mutate(double temp){

	double seqPickProb = temp * 5; // Default is 50/50
	
	List<StringBuilder> newSeqs = new ArrayList<StringBuilder>();
	
	for (StringBuilder s : sequences){
	    double pick = Math.random();
	    StringBuilder copy = copyStringBuilder(s);
	    if (pick < seqPickProb){
		// Mutate the seq
		mutate(copy, temp);
	    }
	    newSeqs.add(copy);
	}

	return new AlignmentSolution(newSeqs);
    }

    public void mutate(StringBuilder s, double temp){
	double gapProb = Align.temp;
	for (int i = 0; i < s.length(); i++){
	    double r = Math.random();
	    if (s.charAt(i) != ' '){ // Prob to add a gap.
		if (r < gapProb) {
		    s.insert(i, ' ');
		}
	    } else { // Prob to remove a gap.
		if (r < gapProb) s.deleteCharAt(i);
	    }
	}
    }

    public double fitness(){

	if (fitnessCalced){
	    //System.out.println("Using precalced fitness.");
	    return storedFitness;
	}
	
	double fitness;
	StringBuilder merged = null;
	
	/* Get the first fitness value. */
	
	fitness = fitnessFromSequences(sequences.get(0), sequences.get(1));
	if (sequences.size() > 2){
	    merged = seqMerge(sequences.get(0), sequences.get(1));
	} else return fitness;

	for (int i = 2; i < sequences.size(); i++){
	    double newFitness = fitnessFromSequences(sequences.get(i), merged);
	    fitness += newFitness;
	    merged = seqMerge(merged, sequences.get(i));
	}
	
	fitnessCalced = true;
	storedFitness = fitness;

	return fitness;
    }

    protected double fitnessFromSequences(StringBuilder seq1, 
					  StringBuilder seq2){
	double seqFitness = 0;
	int max = seq1.length() >= seq2.length() ? 
	    seq1.length() : seq2.length();
	for (int i = 0; i < max; i++){
	    int seq1Ind = 7;
	    int seq2Ind = 7;
	    if (i > 1){
		char s1c1 = (i-1) < seq1.length() ? seq1.charAt(i-1) : ' ';
		char s1c2 =  i    < seq1.length() ? seq1.charAt(i)   : ' ';
		char s2c1 = (i-1) < seq2.length() ? seq2.charAt(i-1) : ' ';
		char s2c2 =  i    < seq2.length() ? seq2.charAt(i)   : ' ';
		if (s1c1 == ' ' && s1c2 == ' '){
		    // Use gap extension.
		    seq1Ind = getMtxIndex('.');
		} else if (s2c1 == ' ' && s2c2 == ' '){
		    // Use gap extension.
		    seq2Ind = getMtxIndex('.');
		} else {
		    seq1Ind = getMtxIndex(i < seq1.length() ? 
					  seq1.charAt(i) : ' ');
		    seq2Ind = getMtxIndex(i < seq2.length() ? 
					  seq2.charAt(i) : ' ');
		}
	    } else {
		seq1Ind = getMtxIndex(i < seq1.length() ? 
				      seq1.charAt(i) : ' ');
		seq2Ind = getMtxIndex(i < seq2.length() ? 
				      seq2.charAt(i) : ' ');
	    }
	    seqFitness += Align.penaltyMatrix.getElement(seq1Ind, seq2Ind);
	}
	return seqFitness;
    }

    protected StringBuilder seqMerge(StringBuilder seq1, StringBuilder seq2){
	int max = seq1.length() >= seq2.length() ? 
	    seq1.length() : seq2.length();
	StringBuilder merged = new StringBuilder((int) (max * 1.5));
	for (int i = 0; i < max; i++){
	    char seq1Char = i < seq1.length() ? seq1.charAt(i) : ' ';
	    char seq2Char = i < seq2.length() ? seq2.charAt(i) : ' ';
	    if (seq1Char == ' ' || seq1Char == '-' || seq1Char == '_'){
		merged.append(seq2Char);
		continue;
	    }
	    if (seq2Char == ' ' || seq2Char == '-' || seq2Char == '_'){
		merged.append(seq1Char);
		continue;
	    }
	    /* Randomly pick the sequence to take from. */
	    int rand = (int)(Math.random() * 2.0);
	    if (rand == 0){
		merged.append(seq1Char);
	    } else {
		merged.append(seq2Char);
	    }
	}
	return merged;
    }

    protected static int getMtxIndex(char c){
	if (Align.acidType){
	    return 0;
	} else {
	    switch (c){
	        case 'A':
		    return 0;
	        case 'T':
		    return 1;
	        case 'C':
		    return 2;
	        case 'G':
		    return 3;
	        case 'U':
		    return 4;
	        case ' ':
	        case '-':
	        case '_':
		    return 5;
	        case '.': // Gap extension
		    return 6;
	        default:
		    return 7;
	    }
	}
    }

    StringBuilder copyStringBuilder(StringBuilder s){
	return new StringBuilder(s.substring(0, s.length()));
    }

    protected int largestLen(){
	int largest = 0;
	for (StringBuilder s : sequences){
	    if (s.length() > largest) largest = s.length();
	}
	return largest;
    }

    public void deleteSharedGaps(){
	for (int i = 0; i < largestLen(); i++){
	    
	}
    }

}
